package com.jintian.smart.kernel.orm.entity.annotation;

import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.orm.entity.DataType;
import org.beetl.sql.annotation.builder.AttributeConvert;
import org.beetl.sql.annotation.builder.UpdateTime;
import org.beetl.sql.clazz.kit.BeanKit;
import org.beetl.sql.clazz.kit.BeetlSQLException;
import org.beetl.sql.core.ExecuteContext;
import org.beetl.sql.core.mapping.BeanProcessor;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class JSONMapperConvert implements AttributeConvert {

    @Override
    public Object toAttr(ExecuteContext ctx, Class cls, String name, ResultSet rs, int index) throws SQLException {
        BeanProcessor processor = ctx.customizedBeanProcessor;
        if (processor == null)
            processor = ctx.sqlManager.getDefaultBeanProcessors();
        Object value = rs.getObject(index);
        if (value == null) {
            return null;
        }
        String content = null;
        try {
            if (value instanceof String)
                content = ((String) value);
            else if (value instanceof byte[]) {
                content = new String((byte[]) value, "utf-8");
            } else if (value instanceof Clob) {
                content = this.clob2String((Clob) value);
            } else if (value instanceof Blob) {
                content = this.blob2String((Blob) value);
            } else {
                content = value.toString();
                // throw new BeetlSQLException(BeetlSQLException.ANNOTATION_DEFINE_ERROR,
                // "不支持 " + value.getClass() + "转换为String类型");
            }

        } catch (Exception e) {
            throw new BeetlSQLException(BeetlSQLException.ANNOTATION_DEFINE_ERROR, value + "转换为String异常", e);
        }
        boolean empty = StringUtil.isEmpty(StringUtil.trim(content));
        ObjectMapper mapper = new ObjectMapper();
        // 允许属性名不带引号
        mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);

        PropertyDescriptor pd = BeanKit.getPropertyDescriptor(cls, name);
        Class<?> targetType = pd.getReadMethod().getReturnType();
        try {
            if (List.class.isAssignableFrom(targetType)) {
                if (empty) {
                    return new ArrayList<>();
                }
                Type type = pd.getReadMethod().getGenericReturnType();
                if (type instanceof ParameterizedType) {
                    final Type itemType = ((ParameterizedType) type).getActualTypeArguments()[0];
                    if (itemType instanceof Class) {
                        List<?> list = mapper.readValue(content, List.class);
                        Object tt = list.stream().map((obj) -> {
                            return mapper.convertValue(obj, (Class<?>) itemType);
                        }).collect(Collectors.toList());
                        return new ArrayList<>((List<?>) tt);
                    }
                }
            } else if (Object.class.equals(targetType)) {
                if (empty) {
                    return new HashMap<>();
                }
                targetType = HashMap.class;
            }
            if (empty) {
                return null;
            }
            return mapper.readValue(content, targetType);
        } catch (Exception e) {
            throw new BeetlSQLException(BeetlSQLException.ANNOTATION_DEFINE_ERROR,
                    "[" + cls.getSimpleName() + "." + name + "]:" + content + "转换为" + targetType + "异常", e);
        }
    }

    @Override
    @UpdateTime
    public Object toDb(ExecuteContext ctx, Class cls, String name, Object pojo) {
        if (pojo == null)
            return null;
        if (!cls.isAssignableFrom(pojo.getClass())) {
            return null;
        }

        Object value = BeanKit.getBeanProperty(pojo, name);
        if (value == null)
            return null;
        JSONMapper object2Map = BeanKit.getAnnotation(cls, name, JSONMapper.class);
        JSONMapper.DataType dbType = object2Map.dataType();
        ObjectMapper mapper = new ObjectMapper();
        // 空值不生成
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 格式化
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        try {
            byte[] bytes = mapper.writeValueAsBytes(value);
            if (dbType.equals(DataType.Text)) {
                return new String(bytes, "utf-8");
            } else if (dbType.equals(DataType.Blob)) {
                return new ByteArrayInputStream(bytes);
            } else
                return bytes;
        } catch (Exception e) {
            throw new BeetlSQLException(BeetlSQLException.ANNOTATION_DEFINE_ERROR, value + "转换为JSON异常", e);
        }
    }

    private String clob2String(Clob clob) throws SQLException {
        if (clob == null) {
            return null;
        }
        return clob.getSubString(1, (int) clob.length());
    }

    private String blob2String(Blob blob) throws SQLException, IOException {
        if (blob == null) {
            return null;
        }
        InputStream in = blob.getBinaryStream();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buff = new byte[8192];
        int len = -1;
        while ((len = in.read(buff, 0, buff.length)) != -1) {
            out.write(buff, 0, len);
        }
        in.close();
        return out.toString("utf-8");
    }
}
